﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExcelAddIn1
{
    class Forecasting_ExponentialSmoothing : Test
    {
        public Forecasting_ExponentialSmoothing(DataContainer data)
        {
            this.data = data;
            res = new List<String>();
        }

        public override string GetInfo()
        {
            String s = "Forecasts a dataset using Exponential Smoothing with α-values between 0.1 and 0.9. The forecast with the lowest Mean Absolute Difference is shown.";
            return s;
        }

        public override void RunTest()
        {
            res.Add("Exponential Smoothing");

            //Error check
            if (data.GetNoSets() != 1)
            {
                res.Add("One dataset is required!");
                return;
            }
            //Error check 2
            if (data.GetDataSet(0).GetN() < 3)
            {
                res.Add("Sample size of minimum 3 is required!");
                return;
            }

            res.Add("α;Forecast;MAD;-");

            double bA = 0.2;
            double bestMAD = 0.0;
            for (double a = 0.1; a <= 0.9; a += 0.1)
            {
                //Find the best MAD value
                double cMAD = RunTest(a, false);
                if (cMAD < bestMAD || bestMAD == 0.0)
                {
                    bA = a;
                    bestMAD = cMAD;
                }
            }

            //Re-run the best forecast to output result.
            RunTest(bA, true);

            res.Add(";;;-");
        }

        public double RunTest(double alpha, bool show)
        {
            DataSet d = data.GetDataSet(0);

            //Create table
            double[,] tab = new double[d.GetN(), 4];
            double prevA = d.GetValue(0);
            double prevF = d.GetValue(0);
            for (int r = 0; r < d.GetN(); r++)
            {
                //Actual
                tab[r, 0] = d.GetValue(r);
                //Forecast
                tab[r, 1] = prevF + alpha * (prevA - prevF);
                //Error
                tab[r, 2] = tab[r, 0] - tab[r, 1];
                //Absolute error
                tab[r, 3] = Math.Abs(tab[r, 2]);

                prevA = tab[r, 0];
                prevF = tab[r, 1];
            }

            //Calculate final forecast
            double finalF = prevF + alpha * (prevA - prevF);
           
            //Calculate Mean Absolute Difference
            double MAD = 0.0;
            int n = 0;
            for (int i = 1; i < d.GetN(); i++)
            {
                MAD += tab[i, 3];
                n++;
            }
            MAD = MAD / (double)n;

            //If show-flag is set, print result.
            if (show) res.Add(alpha.ToString("F1") + ";" + finalF.ToString("F2") + ";" + MAD.ToString("F2"));

            return MAD;
        }
    }
}
